/*
 * ComplexMath.cs
 * 
 * Copyright (c) 2003-2004, dnAnalytics Project. All rights reserved.
*/
namespace dnA.Math {
	///<summary>Provides trigonometric, logarithmic, and other common mathematical functions for complex types</summary>
	public sealed class ComplexMath {
		private ComplexMath() {}

		///<summary>Return the absolute value of a complex type calculated as the euclidean norm</summary>
		public static double Absolute(ComplexDouble value){
			return System.Math.Sqrt( value.Real * value.Real + value.Imag * value.Imag );
		}
        ///<summary>Return the absolute value of a complex type calculated as the euclidean norm</summary>
		public static float Absolute(ComplexFloat value){
			return (float)System.Math.Sqrt( value.Real * value.Real + value.Imag * value.Imag );
		}

		///<summary>Calculate the complex argument of a complex type.  Also commonly refered to as the phase.</summary>
		public static double Argument(ComplexDouble value){
			return System.Math.Atan(value.Imag/value.Real);
		}
        ///<summary>Calculate the complex argument of a complex type.  Also commonly refered to as the phase.</summary>
		public static float Argument(ComplexFloat value){
			return (float)System.Math.Atan(value.Imag/value.Real);
		}

		///<summary>Calculate the 2-argument of a complex type.</summary>
		public static double Argument2(ComplexDouble value) {
			return System.Math.Atan2(value.Imag, value.Real);
		}

		///<summary>Calculate the 2-argument of a complex type.</summary>
		public static float Argument2(ComplexFloat value) {
			return (float)System.Math.Atan2(value.Imag, value.Real);
		}

		///<summary>Return the complex conjugate of a complex type</summary>
		public static ComplexDouble Conjugate(ComplexDouble value){
			return new ComplexDouble(value.Real, -value.Imag);
		}
        ///<summary>Return the complex conjugate of a complex type</summary>
		public static ComplexFloat Conjugate(ComplexFloat value){
			return new ComplexFloat(value.Real, -value.Imag);
		}

		///<summary>Return the complex cosine of a complex type</summary>
		public static ComplexDouble Cos( ComplexDouble value){
			return new ComplexDouble( System.Math.Cos(value.Real) * System.Math.Cosh(value.Imag), -System.Math.Sin(value.Real) * System.Math.Sinh(value.Imag));
		}
        ///<summary>Return the complex cosine of a complex type</summary>
		public static ComplexFloat Cos( ComplexFloat value){
			return new ComplexFloat( (float)(System.Math.Cos(value.Real) * System.Math.Cosh(value.Imag)), -(float)(System.Math.Sin(value.Real) * System.Math.Sinh(value.Imag)));
		}

		///<summary>Return the complex hyperbolic cosine of a complex type</summary>
		public static ComplexDouble Cosh(ComplexDouble value){
			return new ComplexDouble( System.Math.Cos(value.Imag) * System.Math.Cosh(value.Real), System.Math.Sin(value.Imag) * System.Math.Sinh(value.Real));
		}
        ///<summary>Return the complex hyperbolic cosine of a complex type</summary>
		public static ComplexFloat Cosh(ComplexFloat value){
			return new ComplexFloat( (float)(System.Math.Cos(value.Imag) * System.Math.Cosh(value.Real)), (float)(System.Math.Sin(value.Imag) * System.Math.Sinh(value.Real)));
		}

		///<summary>Return the complex exponential of a complex type</summary>
		public static ComplexDouble Exp(ComplexDouble value){
			return new ComplexDouble(System.Math.Exp(value.Real)*System.Math.Cos(value.Imag),System.Math.Exp(value.Real)*System.Math.Sin(value.Imag));
		}
        ///<summary>Return the complex exponential of a complex type</summary>
		public static ComplexFloat Exp(ComplexFloat value){
			return new ComplexFloat((float)(System.Math.Exp(value.Real)*System.Math.Cos(value.Imag)),(float)(System.Math.Exp(value.Real)*System.Math.Sin(value.Imag)));
		}

		///<summary>Return the complex logarithm of a complex type</summary>
		public static ComplexDouble Log(ComplexDouble value){
			double r =  ComplexMath.Absolute(value);
			double i = System.Math.Atan2(value.Imag,value.Real);
			if(i > System.Math.PI) {
				i -= (2.0*System.Math.PI);
			}
			return new ComplexDouble(System.Math.Log(r), i);
		}
        ///<summary>Return the complex logarithm of a complex type</summary>
		public static ComplexFloat Log(ComplexFloat value){
			double r =  ComplexMath.Absolute(value);
			double i = System.Math.Atan2(value.Imag,value.Real);
			if(i > System.Math.PI) {
				i -= (2.0*System.Math.PI);
			}
			return new ComplexFloat((float)System.Math.Log(r), (float)i);
		}
		
		///<summary>Given two complex types return the one with the maximum norm</summary>
		public static ComplexDouble Max(ComplexDouble v1, ComplexDouble v2) {
			if (Norm(v1)>=Norm(v2))
				return v1;
			else
				return v2;
		}
		///<summary>Given two complex types return the one with the maximum norm</summary>
		public static ComplexFloat Max(ComplexFloat v1, ComplexFloat v2) {
			if (Norm(v1)>=Norm(v2))
				return v1;
			else
				return v2;
		}

		///<summary>Return the euclidean norm of a complex type</summary>
		public static double Norm(ComplexDouble value){
			return ComplexMath.Absolute(value);
		}
        ///<summary>Return the euclidean norm of a complex type</summary>
		public static float Norm(ComplexFloat value){
			return ComplexMath.Absolute(value);
		}

		///<summary>Return the polar representation of a complex type</summary>
		public static ComplexDouble Polar(ComplexDouble value){
			return new ComplexDouble(ComplexMath.Absolute(value),System.Math.Atan2(value.Imag, value.Real));
		}
        ///<summary>Return the polar representation of a complex type</summary>
		public static ComplexFloat Polar(ComplexFloat value){
			return new ComplexFloat(ComplexMath.Absolute(value),(float)(System.Math.Atan2(value.Imag, value.Real)));
		}

		///<summary>Returns the sine of a complex type</summary>
		public static ComplexDouble Sin( ComplexDouble value ){
			return new ComplexDouble( System.Math.Sin(value.Real) * System.Math.Cosh(value.Imag), System.Math.Cos(value.Real) * System.Math.Sinh(value.Imag));
		}
        ///<summary>Returns the sine of a complex type</summary>
		public static ComplexFloat Sin( ComplexFloat value ){
			return new ComplexFloat( (float)(System.Math.Sin(value.Real) * System.Math.Cosh(value.Imag)), (float)(System.Math.Cos(value.Real) * System.Math.Sinh(value.Imag)));
		}

		///<summary>Returns the hyperbolic sine of a complex type</summary>
		public static ComplexDouble Sinh(ComplexDouble value){
			return new ComplexDouble( System.Math.Sinh(value.Real) * System.Math.Cos(value.Imag), System.Math.Cosh(value.Real) * System.Math.Sin(value.Imag));
		}
        ///<summary>Returns the hyperbolic sine of a complex type</summary>
		public static ComplexFloat Sinh(ComplexFloat value){
			return new ComplexFloat( (float)(System.Math.Sinh(value.Real) * System.Math.Cos(value.Imag)), (float)(System.Math.Cosh(value.Real) * System.Math.Sin(value.Imag)));
		}

		///<summary>Returns the square root of a complex type</summary>
		public static ComplexDouble Sqrt(ComplexDouble value){
			double r = ComplexMath.Absolute(value);
			double rpart = System.Math.Sqrt( .5 * (r+value.Real));
			double ipart = System.Math.Sqrt( .5 * (r-value.Real));
			if( value.Imag < 0 ) {
				ipart = -ipart;
			}
			return new ComplexDouble( rpart, ipart);
		}
        ///<summary>Returns the square root of a complex type</summary>
		public static ComplexFloat Sqrt(ComplexFloat value){
			double r = ComplexMath.Absolute(value);
			double rpart = System.Math.Sqrt( .5 * (r+value.Real));
			double ipart = System.Math.Sqrt( .5 * (r-value.Real));
			if( value.Imag < 0 ) {
				ipart = -ipart;
			}
			return new ComplexFloat( (float)rpart, (float)ipart);
		}

		///<summary>Returns the tangent of a complex type</summary>
		public static ComplexDouble Tan(ComplexDouble value){
			return ComplexMath.Sin(value)/ComplexMath.Cos(value);
		}
        ///<summary>Returns the tangent of a complex type</summary>
		public static ComplexFloat Tan(ComplexFloat value){
			return ComplexMath.Sin(value)/ComplexMath.Cos(value);
		}

		///<summary>Returns the hyperbolic tangent of a complex type</summary>
		public static ComplexDouble Tanh(ComplexDouble value){
			return ComplexMath.Sinh(value)/ComplexMath.Cosh(value);
		}
        ///<summary>Returns the hyperbolic tangent of a complex type</summary>
		public static ComplexFloat Tanh(ComplexFloat value){
			return ComplexMath.Sinh(value)/ComplexMath.Cosh(value);
		}
		
		/// <summary>Returns the inverse sine of a complex type.</summary>
		public static ComplexDouble Asin(ComplexDouble value){
			ComplexDouble ni = new ComplexDouble(0,-1);
			ComplexDouble i = new ComplexDouble(0,1);
			return ni * ComplexMath.Log(i*value + ComplexMath.Sqrt(1-value*value));
		}

		/// <summary>Returns the inverse sine of a complex type.</summary>
		public static ComplexFloat Asin(ComplexFloat value){
			ComplexFloat ni = new ComplexFloat(0,-1);
			ComplexFloat i = new ComplexFloat(0,1);
			return ni * ComplexMath.Log(i*value + ComplexMath.Sqrt(1-value*value));
		}		
		
		/// <summary>Returns the inverse cosine of a complex type.</summary>
		public static ComplexDouble Acos(ComplexDouble value){
			ComplexDouble ni = new ComplexDouble(0,-1);
			ComplexDouble i = new ComplexDouble(0,1);
			return ni * ComplexMath.Log(value + i*ComplexMath.Sqrt(1-value*value));
		}

		/// <summary>Returns the inverse cosine of a complex type.</summary>
		public static ComplexFloat Acos(ComplexFloat value){
			ComplexFloat ni = new ComplexFloat(0,-1);
			ComplexFloat i = new ComplexFloat(0,1);
			return ni * ComplexMath.Log(value + i*ComplexMath.Sqrt(1-value*value));
		}		

		/// <summary>Returns the inverse tangent of a complex type.</summary>
		public static ComplexDouble Atan(ComplexDouble value){
			ComplexDouble i = new ComplexDouble(0,1);
			return i/2 * ComplexMath.Log((i+value)/(i-value));
		}

		/// <summary>Returns the inverse tangent of a complex type.</summary>
		public static ComplexFloat Atan(ComplexFloat value){
			ComplexFloat i = new ComplexFloat(0,1);
			return i/2 * ComplexMath.Log((i+value)/(i-value));
		}
		
		///<summary>Returns the inverse hyperbolic sine of a complex type</summary>
		public static ComplexDouble Asinh(ComplexDouble value){
			return -ComplexMath.Log(ComplexMath.Sqrt(1+value*value)-value);
		}

		///<summary>Returns the inverse hyperbolic sine of a complex type</summary>
		public static ComplexFloat Asinh(ComplexFloat value){
			return -ComplexMath.Log(ComplexMath.Sqrt(1+value*value)-value);
		}		
		
		///<summary>Returns the inverse hyperbolic cosine of a complex type</summary>
		public static ComplexDouble Acosh(ComplexDouble value){
			ComplexFloat i = new ComplexFloat(0,1);
			return ComplexMath.Log(value + i*ComplexMath.Sqrt(1-value*value));
		}

		///<summary>Returns the inverse hyperbolic cosine of a complex type</summary>
		public static ComplexFloat Acosh(ComplexFloat value){
			ComplexFloat i = new ComplexFloat(0,1);
			return ComplexMath.Log(value + i*ComplexMath.Sqrt(1-value*value));
		}		

		///<summary>Returns the inverse hyperbolic tangent of a complex type</summary>
		public static ComplexDouble Atanh(ComplexDouble value){
			return .5 * ComplexMath.Log((1+value)/(1-value));
		}

		///<summary>Returns the inverse hyperbolic tangent of a complex type</summary>
		public static ComplexFloat Atanh(ComplexFloat value){
			return .5f * ComplexMath.Log((1+value)/(1-value));
		}
	}
}

